package com.xiaohang.springstudy.chapter1;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaohang.springstudy.chapter1.entity.UserDo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;

import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)// 引入spring对JUnit的支持
@SpringBootTest(classes = Chapter1Application.class)// 指定springboot启动类
@WebAppConfiguration// 测试环境使用，用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的；value指定web应用的根；
@ActiveProfiles(profiles = "dev")
// 使每个测试方法独立有效，不相互影响
@Transactional
/**
 * 使用MockM模拟Http请求测试，推荐使用MockMvcBuilders.webAppContextSetup(context).build();形式
 */
public class Chapter1ApplicationTest1 {

    private MockMvc mvc;// 模拟controller请求发起
    @Autowired
    // 注入上下文控制器
    private WebApplicationContext context;

    @Before
    // MockMvcBuilder是用来构造MockMvc的构造器，其主要有两个实现：StandaloneMockMvcBuilder和DefaultMockMvcBuilder，分别对应两种测试方式，即独立安装和集成Web环境测试（此种方式并不会集成真正的web环境，而是通过相应的Mock API进行模拟测试，无须启动服务器）。
    public void setUp() {
        mvc = MockMvcBuilders.webAppContextSetup(context).build();
    }

    /**
     * perform：执行一个RequestBuilder请求，会自动执行SpringMVC的流程并映射到相应的控制器执行处理；
     * andExpect：添加ResultMatcher验证规则，验证控制器执行完成后结果是否正确；
     * andDo：添加ResultHandler结果处理器，比如调试时打印结果到控制台；
     * andReturn：最后返回相应的MvcResult；然后进行自定义验证/进行下一步的异步处理；
     */
    @Test
    public void testUserController() throws Exception {
        // 1、测试查询User列表
        mvc.perform(MockMvcRequestBuilders.get("/api/v1/users/").accept(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk()).andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"zhangsan\",\"age\":12},{\"id\":2,\"name\":\"lisi\",\"age\":12},{\"id\":3,\"name\":\"wangwu\",\"age\":12}]"))).andDo(MockMvcResultHandlers.print());
    }

    @Test
    public void testUserControlle1() throws Exception {
        // 2、测试根据ID查找学生
        mvc.perform(MockMvcRequestBuilders.get("/api/v1/users/1/queryUserInfo")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string(equalTo("{\"id\":1,\"name\":\"zhangsan\",\"age\":12}"))).andExpect(jsonPath("name").value("zhangsan")).andDo(MockMvcResultHandlers.print());
    }

    @Test
    public void testUserController2() throws Exception {
        // 3、修改ID=1的学生信息
        UserDo userDo = new UserDo(1L, "fan", 26);
        // @RequestBody请求可以转换数据内容为Json进行请求
        mvc.perform(MockMvcRequestBuilders.put("/api/v1/users/").contentType(MediaType.APPLICATION_JSON_UTF8).content(new ObjectMapper().writeValueAsString(userDo))).andExpect(content().string("success")).andDo(MockMvcResultHandlers.print());
        // 学生信息已经被更新
        mvc.perform(MockMvcRequestBuilders.get("/api/v1/users/")).andExpect(content().string("[{\"id\":1,\"name\":\"fan\",\"age\":26},{\"id\":2,\"name\":\"lisi\",\"age\":12},{\"id\":3,\"name\":\"wangwu\",\"age\":12}]")).andDo(MockMvcResultHandlers.print());
    }

    @Test
    public void testUserController3() throws Exception {
        // 4、根据ID删除学生
        mvc.perform(MockMvcRequestBuilders.delete("/api/v1/users/1/delUserInfo")).andExpect(content().string("success"));
        // zhangsan这个人已经不存在了
        mvc.perform(MockMvcRequestBuilders.get("/api/v1/users/")).andExpect(jsonPath("zhangsan").doesNotExist()).andDo(MockMvcResultHandlers.print());
    }
}